﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using RainEngine.Rendering;
using RainEngine.SceneManagement;
using RainEngine.Effects;

namespace RainEngine.SceneNodes
{
    public class BillboardSceneNode:Renderable3DSceneNode  
    {
        /// <summary>
        /// 包含Billboard顶点信息的Mesh数据。
        /// </summary>
        Mesh mesh;   
        
        /// <summary>
        /// BillBoardEffect使用的Effect
        /// </summary>
        BillBoardEffect effect;

        /// <summary>
        /// 获取illBoardEffect使用的Effect
        /// </summary>
        public BillBoardEffect Effect
        {
            get { return effect; }
        }

        /// <summary>
        /// 是否启用遮蔽，默认为true
        /// </summary>
        public bool EnsureOcclusion = true;

        /// <summary>
        /// Billboard有两种模式：球形和圆柱形
        /// </summary>
        public enum BillboardMode { Cylindrical, Spherical };
        
        /// <summary>
        /// 初始模式为圆柱形
        /// </summary>
        public BillboardMode Mode = BillboardMode.Cylindrical ;

        /// <summary>
        /// 创建一个BillboardSystem，它可以批量绘制使用同一张纹理的billboard
        /// </summary>
        /// <param name="engine">引擎</param>
        /// <param name="setScene">场景</param>
        /// <param name="setTextureName">带路径的纹理名称</param>
        public BillboardSceneNode(GraphicsDevice graphicsDevice, ContentManager setContent, Scene setScene, string setTextureName)
            : base(graphicsDevice, setContent, setScene)
        {
            effect = new BillBoardEffect(graphicsDevice );
            
            if (!string.IsNullOrEmpty(setTextureName))
                effect.Texture = content.Load<Texture2D>(setTextureName);            
        }

        /// <summary>
        /// 初始化Billboard，创建Billboard的顶点缓冲和索引缓冲。 
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
            this.UpdateOrder = SceneNodeOrdering.TransparentNode.GetValue();
            this.mesh = MeshBuilder.CreatBillBoard(graphicsDevice);
        }

        public override int Draw(GameTime gameTime, Matrix ViewMatrix, Matrix ProjectionMatrix, Vector3 CameraPosition)
        {
            graphicsDevice.BlendState = BlendState.AlphaBlend;
            
            mesh.PrepareRender(graphicsDevice);
            effect.World = Pose.WorldMatrix;
            effect.View = ViewMatrix;
            effect.Projection = ProjectionMatrix;
            effect.EyePosition = CameraPosition;
            effect.Up=(Mode == BillboardMode.Spherical ? Scene.Camera.Up : Vector3.Up);

            if (EnsureOcclusion)
            {
                drawOpaquePixels(gameTime, ViewMatrix, ProjectionMatrix, CameraPosition);
                drawTransparentPixels(gameTime, ViewMatrix, ProjectionMatrix, CameraPosition);
            }
            else
            {
                graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
                effect.AlphaTest = false;
                drawBillboards(gameTime, ViewMatrix, ProjectionMatrix, CameraPosition);
            }

            // 重置渲染状态
            graphicsDevice.BlendState = BlendState.Opaque;
            graphicsDevice.DepthStencilState = DepthStencilState.Default;

            return 2;
        }

        /// <summary>
        /// 绘制不透明的颜色
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="ViewMatrix"></param>
        /// <param name="ProjectionMatrix"></param>
        /// <param name="CameraPosition"></param>
        void drawOpaquePixels(GameTime gameTime, Matrix ViewMatrix, Matrix ProjectionMatrix, Vector3 CameraPosition)
        {
            graphicsDevice.DepthStencilState = DepthStencilState.Default;
            effect.AlphaTest = true;
            effect.AlphaTestGreater = true;
            drawBillboards  (gameTime, ViewMatrix, ProjectionMatrix, CameraPosition);
            
        }

        /// <summary>
        /// 绘制透明颜色
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="ViewMatrix"></param>
        /// <param name="ProjectionMatrix"></param>
        /// <param name="CameraPosition"></param>
        void drawTransparentPixels(GameTime gameTime, Matrix ViewMatrix, Matrix ProjectionMatrix, Vector3 CameraPosition)
        {
            graphicsDevice.DepthStencilState = DepthStencilState.DepthRead;
            effect.AlphaTest = true;            
            effect.AlphaTestGreater = false;
            drawBillboards (gameTime, ViewMatrix, ProjectionMatrix, CameraPosition);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="ViewMatrix"></param>
        /// <param name="ProjectionMatrix"></param>
        /// <param name="CameraPosition"></param>
        void drawBillboards(GameTime gameTime, Matrix ViewMatrix, Matrix ProjectionMatrix, Vector3 CameraPosition)
        {
            effect.CurrentTechnique .Passes[0].Apply();
            mesh.Draw(graphicsDevice);
        }        

        public override void SetClipPlane(Vector4? Plane)
        {
            effect.ClipPlane = Plane.Value;
        }
    }
}
